package net.hands.ado.db;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;

import net.hands.ado.ADOException;
import net.hands.ado.AbstractADOManager;
import net.hands.ado.IADOListener;
import net.hands.ado.db.event.IDBListener;
import net.hands.ado.db.jdbc.DatabaseMeta;
import net.hands.ado.db.jdbc.IBatchValueSetter;
import net.hands.ado.db.jdbc.IJdbcProvider;
import net.hands.ado.db.jdbc.IQueryExtractor;
import net.hands.ado.db.jdbc.JdbcUtils;
import net.hands.common.coll.KVMap;
import net.hands.common.trans.TransactionObjectCallback;

public abstract class AbstractDBManager extends AbstractADOManager implements
		IDBManager {

	protected IJdbcProvider jdbcProvider;

	public AbstractDBManager setDbManagerFactory(final IJdbcProvider provider) {
		this.jdbcProvider = provider;
		return this;
	}

	@Override
	public Map<String, Object> executeQuery(SQLValue sqlVal) {
		return createQueryMap(sqlVal);
	}

	@Override
	public IDBDataQuery executeQuerySet(SQLValue sqlVal) {
		return createQueryMapSet(sqlVal);
	}

	protected DBDataQuery createQueryMapSet(final SQLValue sqlVal) {
		return new DBDataQuery(jdbcProvider, sqlVal) {

		};
	}

	protected int executeUpdate(final String sql) {
		return executeUpdate(new SQLValue(sql));
	}

	protected int executeUpdate(final SQLValue sqlVal) {
		return jdbcProvider.doUpdate(sqlVal);
	}

	@Override
	public int execute(final SQLValue... sqlValues) {
		return execute(null, sqlValues);
	}

	@Override
	public int execute(final IDBListener l, final SQLValue... sqlValues) {
		if (sqlValues == null || sqlValues.length == 0) {
			return 0;
		}
		if (l != null) {
			l.onBeforeExecute(this, sqlValues);
		}
		final Collection<IADOListener> listeners = getListeners();
		for (final IADOListener listener : listeners) {
			((IDBListener) listener).onBeforeExecute(this, sqlValues);
		}
		int ret = 0;
		for (final SQLValue sqlVal : sqlValues) {
			ret += executeUpdate(sqlVal);
		}
		if (l != null) {
			l.onAfterExecute(this, sqlValues);
		}
		for (final IADOListener listener : listeners) {
			((IDBListener) listener).onAfterExecute(this, sqlValues);
		}
		return ret;
	}

	@Override
	public int[] batchUpdate(final String... sqlArr) {
		return jdbcProvider.doBatch(sqlArr);
	}

	@Override
	public int[] batchUpdate(final String sql, final int batchCount,
			final IBatchValueSetter setter) {
		return jdbcProvider.doBatch(sql, batchCount, setter);
	}

	@Override
	public int executeTransaction(final SQLValue... sqlValues) {
		return executeTransaction(null, sqlValues);
	}

	@Override
	public int executeTransaction(final IDBListener l,
			final SQLValue... sqlValues) {
		return doExecuteTransaction(new TransactionObjectCallback<Integer>() {

			@Override
			public Integer onTransactionCallback() throws ADOException {
				return execute(l, sqlValues);
			}
		});
	}

	protected Map<String, Object> createQueryMap(final SQLValue sqlVal) {
		return jdbcProvider.queryObject(sqlVal,
				new IQueryExtractor<Map<String, Object>>() {

					@Override
					public Map<String, Object> extractData(final ResultSet rs)
							throws SQLException, ADOException {
						return rs.next() ? mapRowData(rs) : null;
					}
				});
	}

	protected Map<String, Object> mapRowData(final ResultSet rs)
			throws SQLException {
		final ResultSetMetaData rsmd = rs.getMetaData();
		final int columnCount = rsmd.getColumnCount();
		final Map<String, Object> mapData = new KVMap(columnCount)
				.setCaseInsensitive(true);
		for (int i = 1; i <= columnCount; i++) {
			String key;
			Object obj;
			key = JdbcUtils.lookupColumnName(rsmd, i);
			obj = jdbcProvider.getJdbcDialect().getResultSetValue(rs, i);
			mapData.put(key, obj);
		}
		return mapData;
	}

	@Override
	public DatabaseMeta getDatabaseMeta() {
		return jdbcProvider.getDatabaseMeta();
	}

}
